#target "indesign"
// Export INDDs to PDF from either a path list or selected folders, logging results beside the PDFs.
(function () {
    if (!app || app.name !== "Adobe InDesign") {
        alert("请在 Adobe InDesign 中运行此脚本。");
        return;
    }

    var pdfPreset = choosePdfPreset();
    if (!pdfPreset) {
        alert("未选择任何 PDF 预设，操作已取消。");
        return;
    }

    var sourceMode = chooseSourceMode();
    if (!sourceMode) {
        alert("未选择导出来源，操作已取消。");
        return;
    }

    var job = sourceMode === "list" ? prepareListJob() : prepareFolderJob();
    if (!job) {
        return;
    }

    job.outputFolder = selectOutputFolder();
    var filesToProcess = job.files;
    var skippedMissing = job.skippedMissing.slice(0);

    var skippedInUse = [];
    var failed = [];
    var linkIssues = [];
    var exportedCount = 0;
    var exportedDetails = [];
    var pdfFolders = {};

    var originalInteraction = app.scriptPreferences.userInteractionLevel;
    app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;

    try {
        var availability = analyzeDocumentAvailability(filesToProcess);
        var proceed = false;
        if (availability.inUse.length) {
            var notify = "检测到以下文档无法打开，可能正被其他人使用：\n" +
                availability.inUse.join("\n") +
                "\n这些文件将在导出时跳过。是否继续导出其他文件？";
            proceed = confirm(notify);
        } else if (filesToProcess.length) {
            proceed = confirm("未检测到被占用的文档。是否开始导出？");
        } else {
            proceed = confirm("没有找到可导出的 INDD 文件，仍然创建日志吗？");
        }

        if (!proceed) {
            alert("导出操作已取消。");
            return;
        }

        for (var i = 0; i < filesToProcess.length; i++) {
            var inddFile = filesToProcess[i];
            if (!inddFile.exists) {
                skippedMissing.push(inddFile.fsName);
                continue;
            }

            var doc = findOpenDocument(inddFile);
            var openedHere = false;

            try {
                if (!doc) {
                    try {
                        doc = app.open(inddFile, false);
                        openedHere = true;
                    } catch (_openError) {
                        skippedInUse.push(inddFile.fsName);
                        continue;
                    }
                }

                var linkReport = refreshLinks(doc);
                if (linkReport.missing.length || linkReport.failedUpdates.length) {
                    linkIssues.push(inddFile.fsName + "\n  缺失链接: " + linkReport.missing.length + "\n  更新失败: " + linkReport.failedUpdates.length);
                }

                var pdfFile = job.determineOutputFile(inddFile, job.outputFolder, job.rootFolders);
                ensureFolderExists(pdfFile.parent);

                doc.exportFile(ExportFormat.PDF_TYPE, pdfFile, false, pdfPreset);
                exportedCount++;
                exportedDetails.push(inddFile.fsName + " -> " + pdfFile.fsName);
                pdfFolders[pdfFile.parent.fsName] = pdfFile.parent;
            } catch (fileError) {
                failed.push(inddFile.fsName + " → " + fileError);
            } finally {
                if (openedHere && doc && doc.isValid) {
                    doc.close(SaveOptions.NO);
                }
            }
        }
    } finally {
        app.scriptPreferences.userInteractionLevel = originalInteraction;
    }

    var outputSummary = job.outputFolder ? job.outputFolder.fsName : "与源 INDD 文件相同位置";
    var summary = "成功导出 " + exportedCount + " 个 PDF（使用预设：" + pdfPreset.name + "）。" +
        "\n来源：" + job.sourceDescription +
        "\n输出位置：" + outputSummary;

    if (failed.length) {
        summary += "\n以下文件导出失败：\n" + failed.join("\n");
    }
    if (skippedMissing.length) {
        summary += "\n以下路径不存在或不可访问：\n" + skippedMissing.join("\n");
    }
    if (skippedInUse.length) {
        summary += "\n以下文件因正被使用而跳过：\n" + skippedInUse.join("\n");
    }
    if (linkIssues.length) {
        summary += "\n部分文件的链接存在问题：\n" + linkIssues.join("\n");
    }

    var logContent = buildLogContent({
        presetName: pdfPreset.name,
        sourceDescription: job.sourceDescription,
        outputDescription: outputSummary,
        exportedCount: exportedCount,
        exportedDetails: exportedDetails,
        failed: failed,
        skippedMissing: skippedMissing,
        skippedInUse: skippedInUse,
        linkIssues: linkIssues
    });

    var fallbackLogFolder = job.outputFolder ? job.outputFolder : job.fallbackLogFolder;
    writeLogFiles(pdfFolders, fallbackLogFolder, logContent);

    alert(summary);

    function choosePdfPreset() {
        if (!app.pdfExportPresets || app.pdfExportPresets.length === 0) {
            alert("当前没有可用的 PDF 导出预设。");
            return null;
        }

        var presetNames = app.pdfExportPresets.everyItem().name;
        if (!presetNames || !presetNames.length) {
            alert("未能读取 PDF 导出预设列表。");
            return null;
        }

        var dialog = new Window("dialog", "选择 PDF 导出预设");
        dialog.orientation = "column";
        dialog.alignChildren = "fill";

        dialog.add("statictext", undefined, "请选择 PDF 导出预设：");
        var presetList = dialog.add("dropdownlist", undefined, presetNames);
        presetList.minimumSize.width = 260;
        if (presetList.items.length) {
            presetList.selection = 0;
        }

        var buttonGroup = dialog.add("group");
        buttonGroup.alignment = "right";
        buttonGroup.add("button", undefined, "确定", { name: "ok" });
        buttonGroup.add("button", undefined, "取消", { name: "cancel" });

        var result = dialog.show();
        if (result !== 1 || !presetList.selection) {
            return null;
        }

        var chosenName = presetList.selection.text;
        try {
            var preset = app.pdfExportPresets.itemByName(chosenName);
            preset.name;
            return preset;
        } catch (_error) {
            alert("无法获取所选预设：" + chosenName);
            return null;
        }
    }

    function chooseSourceMode() {
        var dialog = new Window("dialog", "选择导出来源");
        dialog.orientation = "column";
        dialog.alignChildren = "fill";

        dialog.add("statictext", undefined, "请选择 INDD 文件来源：");

        var listRadio = dialog.add("radiobutton", undefined, "使用文本文件中的路径列表");
        var foldersRadio = dialog.add("radiobutton", undefined, "选择一个或多个文件夹");
        listRadio.value = true;

        var buttons = dialog.add("group");
        buttons.alignment = "right";
        buttons.add("button", undefined, "确定", { name: "ok" });
        buttons.add("button", undefined, "取消", { name: "cancel" });

        var result = dialog.show();
        if (result !== 1) {
            return null;
        }
        return listRadio.value ? "list" : "folders";
    }

    function prepareListJob() {
        var listFile = File.openDialog("请选择包含 INDD 路径的文本文件", "*.txt");
        if (!listFile) {
            alert("未选择列表文件。");
            return null;
        }

        var paths = readPaths(listFile);
        if (!paths.length) {
            alert("列表文件中未找到有效的 INDD 路径。");
            return null;
        }

        var files = [];
        var missing = [];
        var seen = {};

        for (var i = 0; i < paths.length; i++) {
            var pathString = paths[i];
            if (!pathString) {
                continue;
            }
            var normalized = normalizePath(pathString);
            if (seen[normalized]) {
                continue;
            }
            seen[normalized] = true;

            var inddFile = File(pathString);
            if (inddFile.exists) {
                files.push(inddFile);
            } else {
                missing.push(pathString);
            }
        }

        return {
            mode: "list",
            sourceDescription: "路径列表：" + listFile.fsName,
            files: files,
            rootFolders: null,
            skippedMissing: missing,
            fallbackLogFolder: listFile.parent,
            determineOutputFile: determineFromList
        };
    }

    function prepareFolderJob() {
        var folders = promptForFolders();
        if (!folders.length) {
            alert("未选择任何文件夹。");
            return null;
        }

        var includeSubfolders = confirm("是否包含子文件夹中的 INDD 文件？\n选择“确定”为包含，选择“取消”为仅限当前文件夹。");
        var files = [];
        var seen = {};

        for (var i = 0; i < folders.length; i++) {
            collectInddFiles(folders[i], files, includeSubfolders, seen);
        }

        if (!files.length) {
            alert("所选位置中未找到 INDD 文件。");
            return null;
        }

        var description = [];
        for (var j = 0; j < folders.length; j++) {
            description.push(folders[j].fsName);
        }

        return {
            mode: "folders",
            sourceDescription: "起始文件夹：\n" + description.join("\n"),
            files: files,
            rootFolders: folders,
            skippedMissing: [],
            fallbackLogFolder: folders[0],
            determineOutputFile: determineFromFolders
        };
    }

    function determineFromList(inddFile, outputFolder) {
        if (!outputFolder) {
            return File(inddFile.fsName.replace(/\.indd$/i, ".pdf"));
        }
        return File(outputFolder.fsName + "/" + inddFile.displayName.replace(/\.indd$/i, ".pdf"));
    }

    function determineFromFolders(inddFile, outputFolder, roots) {
        if (!outputFolder) {
            return File(inddFile.fsName.replace(/\.indd$/i, ".pdf"));
        }
        var info = getRelativeOutputInfo(inddFile, roots);
        var targetFolder = outputFolder;
        if (info.folderPath) {
            targetFolder = new Folder(outputFolder.fsName + "/" + info.folderPath);
        }
        return File(targetFolder.fsName + "/" + info.fileName + ".pdf");
    }

    function readPaths(file) {
        var result = [];
        if (!file.open("r")) {
            alert("无法读取列表文件：" + file.fsName);
            return result;
        }

        while (!file.eof) {
            var line = file.readln();
            var cleaned = parsePathLine(line);
            if (cleaned) {
                result.push(cleaned);
            }
        }

        file.close();
        return result;
    }

    function parsePathLine(line) {
        if (!line) {
            return null;
        }
        var trimmed = line.replace(/^\s+|\s+$/g, "");
        if (!trimmed) {
            return null;
        }
        if (trimmed.charAt(0) === "\"") {
            trimmed = trimmed.replace(/^\"+|\"+$/g, "");
        }
        return trimmed.replace(/\\/g, "/");
    }

    function selectOutputFolder() {
        if (!confirm("是否选择自定义的 PDF 输出文件夹？\n选择“确定”进行选择，选择“取消”将导出到源文件所在位置。")) {
            return null;
        }

        var folder = Folder.selectDialog("请选择 PDF 输出文件夹");
        if (!folder) {
            alert("未选择输出文件夹，将导出到源文件所在位置。");
            return null;
        }
        return folder;
    }

    function promptForFolders() {
        var result = [];
        while (true) {
            var folder = Folder.selectDialog("请选择包含 INDD 文件的文件夹");
            if (!folder) {
                break;
            }
            if (!folderAlreadyAdded(result, folder)) {
                result.push(folder);
            }
            if (!confirm("是否继续选择其他文件夹？")) {
                break;
            }
        }
        return result;
    }

    function folderAlreadyAdded(list, folder) {
        for (var i = 0; i < list.length; i++) {
            if (list[i].fsName === folder.fsName) {
                return true;
            }
        }
        return false;
    }

    function collectInddFiles(folder, collector, includeSubfolders, seen) {
        var entries = folder.getFiles();
        for (var i = 0; i < entries.length; i++) {
            var item = entries[i];
            if (item instanceof Folder) {
                if (includeSubfolders) {
                    collectInddFiles(item, collector, includeSubfolders, seen);
                }
            } else if (item instanceof File && /\.indd$/i.test(item.displayName)) {
                var normalized = normalizePath(item.fsName);
                if (!seen[normalized]) {
                    seen[normalized] = true;
                    collector.push(File(item));
                }
            }
        }
    }

    function analyzeDocumentAvailability(files) {
        var result = {
            openable: [],
            inUse: []
        };

        for (var i = 0; i < files.length; i++) {
            var file = files[i];
            if (!file.exists) {
                continue;
            }
            var existingDoc = findOpenDocument(file);
            if (existingDoc) {
                result.openable.push(file);
                continue;
            }

            var tempDoc = null;
            try {
                tempDoc = app.open(file, false);
                result.openable.push(file);
            } catch (_error) {
                result.inUse.push(file.fsName);
            } finally {
                if (tempDoc && tempDoc.isValid) {
                    tempDoc.close(SaveOptions.NO);
                }
            }
        }

        return result;
    }

    function findOpenDocument(inddFile) {
        for (var i = 0; i < app.documents.length; i++) {
            var currentDoc = app.documents[i];
            try {
                if (currentDoc.fullName && currentDoc.fullName.fsName === inddFile.fsName) {
                    return currentDoc;
                }
            } catch (_ignored) {}
        }
        return null;
    }

    function refreshLinks(doc) {
        var missing = [];
        var failedUpdates = [];

        var docLinks = doc.links;
        for (var i = 0; i < docLinks.length; i++) {
            var link = docLinks[i];
            try {
                if (link.status === LinkStatus.LINK_OUT_OF_DATE) {
                    link.update();
                } else if (link.status === LinkStatus.LINK_MISSING) {
                    missing.push(link.name);
                }
            } catch (linkError) {
                failedUpdates.push(link.name + " → " + linkError);
            }
        }

        return {
            missing: missing,
            failedUpdates: failedUpdates
        };
    }

    function ensureFolderExists(folder) {
        if (!folder || folder.exists) {
            return;
        }
        var parent = folder.parent;
        if (parent && !parent.exists) {
            ensureFolderExists(parent);
        }
        folder.create();
    }

    function writeLogFiles(folderMap, fallbackFolder, content) {
        var targets = [];
        for (var key in folderMap) {
            if (folderMap.hasOwnProperty(key)) {
                targets.push(folderMap[key]);
            }
        }

        if (!targets.length && fallbackFolder) {
            targets.push(fallbackFolder);
        }
        if (!targets.length) {
            return;
        }

        var timestamp = formatTimestamp(new Date());
        var fileName = "PDF_Export_Log_" + timestamp + ".txt";

        for (var i = 0; i < targets.length; i++) {
            var folder = targets[i];
            if (!folder) {
                continue;
            }
            ensureFolderExists(folder);
            var logFile = File(folder.fsName + "/" + fileName);
            if (logFile.exists) {
                try {
                    logFile.remove();
                } catch (_ignore) {}
            }
            if (logFile.open("w")) {
                logFile.encoding = "UTF-8";
                logFile.write(content);
                logFile.close();
            }
        }
    }

    function buildLogContent(info) {
        var newline = "\r\n";
        var lines = [];
        var timestamp = formatDateTime(new Date());

        lines.push("PDF 导出日志");
        lines.push("时间: " + timestamp);
        lines.push("使用预设: " + info.presetName);
        lines.push("来源: " + info.sourceDescription);
        lines.push("输出位置: " + info.outputDescription);
        lines.push("");

        lines.push("成功导出 (" + info.exportedCount + "):");
        if (info.exportedDetails.length) {
            lines = lines.concat(info.exportedDetails);
        } else {
            lines.push("无");
        }
        lines.push("");

        lines.push("导出失败 (" + info.failed.length + "):");
        if (info.failed.length) {
            lines = lines.concat(info.failed);
        } else {
            lines.push("无");
        }
        lines.push("");

        lines.push("缺失文件 (" + info.skippedMissing.length + "):");
        if (info.skippedMissing.length) {
            lines = lines.concat(info.skippedMissing);
        } else {
            lines.push("无");
        }
        lines.push("");

        lines.push("被占用文件 (" + info.skippedInUse.length + "):");
        if (info.skippedInUse.length) {
            lines = lines.concat(info.skippedInUse);
        } else {
            lines.push("无");
        }
        lines.push("");

        lines.push("链接问题 (" + info.linkIssues.length + "):");
        if (info.linkIssues.length) {
            lines = lines.concat(info.linkIssues);
        } else {
            lines.push("无");
        }

        return lines.join(newline) + newline;
    }

    function getRelativeOutputInfo(inddFile, roots) {
        var filePath = normalizePath(inddFile.fsName);
        var fileNameOnly = inddFile.displayName.replace(/\.indd$/i, "");

        for (var i = 0; i < roots.length; i++) {
            var rootPath = normalizePath(roots[i].fsName);
            if (rootPath.charAt(rootPath.length - 1) !== "/") {
                rootPath += "/";
            }

            var rootLower = rootPath.toLowerCase();
            var fileLower = filePath.toLowerCase();
            if (fileLower.indexOf(rootLower) === 0) {
                var relative = filePath.substring(rootPath.length);
                var lastSlash = relative.lastIndexOf("/");
                var folderPart = lastSlash > -1 ? relative.substring(0, lastSlash) : "";
                var namePart = lastSlash > -1 ? relative.substring(lastSlash + 1) : relative;
                namePart = namePart.replace(/\.indd$/i, "");
                return {
                    folderPath: folderPart,
                    fileName: namePart || fileNameOnly
                };
            }
        }

        return {
            folderPath: "",
            fileName: fileNameOnly
        };
    }

    function normalizePath(path) {
        return String(path).replace(/\\/g, "/");
    }

    function formatTimestamp(date) {
        return date.getFullYear().toString() +
            padNumber(date.getMonth() + 1) +
            padNumber(date.getDate()) + "_" +
            padNumber(date.getHours()) +
            padNumber(date.getMinutes()) +
            padNumber(date.getSeconds());
    }

    function formatDateTime(date) {
        return date.getFullYear() + "-" +
            padNumber(date.getMonth() + 1) + "-" +
            padNumber(date.getDate()) + " " +
            padNumber(date.getHours()) + ":" +
            padNumber(date.getMinutes()) + ":" +
            padNumber(date.getSeconds());
    }

    function padNumber(value) {
        return value < 10 ? "0" + value : value.toString();
    }
})();
